package org.syntaxlisp.core;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.syntaxlisp.core.element.*;
import org.syntaxlisp.core.match.MatchException;
import org.syntaxlisp.core.utils.*;

import java.io.FileNotFoundException;
import java.util.*;
import java.util.function.Function;

import static org.syntaxlisp.core.element.SyntaxLispElementType.*;

/**
 * @author HuangYijun
 * @data 2023-02-08 16:27
 **/
public class PreMatchingProcess {

    private PreMatchingProcess() { }

    public static Pair<Map<String, SyntaxLispElement>, Collection<String>>
    parseLangEnv(String langPath, Collection<String> entries)
            throws FileNotFoundException {
        List<Element> langSyntaxElements = LispListParser.parse(LispStyleAccess.readAll(langPath));
        return SyntaxLispParser.parse(langSyntaxElements, entries);
    }

    /**
     *
     * @param env
     */
    public static void checkDeadFirstRefReduceCycle (Map<String, SyntaxLispElement> env) {
        env.forEach((def, element) -> {
            List<String> refPath = new ArrayList<>();
            if (hasDeadFirstRefReduceCycle(def, refPath, env)) {
                throw new MatchException("发现 FIRST 首引用归约死循环：" + refPath);
            }
        });
    }

    public static boolean hasDeadFirstRefReduceCycle (String curDef,
                                                      List<String> refPath,
                                                      Map<String, SyntaxLispElement> env) {
        if (refPath.contains(curDef)) {
            List<String> cycle = new ArrayList<>(refPath.subList(refPath.indexOf(curDef), refPath.size()));
            refPath.clear();
            refPath.addAll(cycle);
            return true;
        }
        refPath.add(curDef);
        for (SyntaxLispElement dpf : env.get(curDef).getDirectPf()) {
            if (SyntaxLispElementType.REF == dpf.getType()) {
                // 不能仅仅只判断 dpf.isCanSkipToUpperNext()，应该判断能否直接 reduce
                if (canReduceToDefDirectly(dpf)
                        && hasDeadFirstRefReduceCycle(dpf.getVal(), refPath, env)) {
                    return true;
                }
            }
        }
        refPath.remove(curDef);
        return false;
    }

    public static boolean canReduceToDefDirectly(SyntaxLispElement cur) {
        return cur.getUpper() == null
                || (cur.isCanSkipToUpperNext() && canReduceToDefDirectly(cur.getUpper()));
    }

    /**
     * 计算能够收敛到当前 Def 的 First Ref 集合
     * @param env environment
     * @return
     */
    public static Map<DefID, Set<DefID>> firstRefReduceMap(Map<String, SyntaxLispElement> env,
                                                           Map<String, DefID> defIDMap) {
        Map<DefID, Set<DefID>> res = new HashMap<>();
        env.forEach((def, element) -> {
            DefID curDef = defIDMap.get(def);
            res.put(curDef, firstRefReduceMap0(curDef, env, defIDMap));
        });
        return res;
    }

    public static Set<DefID> firstRefReduceMap0(DefID def,
                                                 Map<String, SyntaxLispElement> env,
                                                 Map<String, DefID> defIDMap) {
        Set<DefID> res = new HashSet<>();
        Queue<DefID> next = new ArrayDeque<>();
        next.add(def);
        while (!next.isEmpty()) {
            int size = next.size();
            for (int i = 0; i < size; i ++) {
                DefID curDef = next.poll();
                env.get(curDef.getDef()).getDirectPf()
                   .forEach(dpf -> {
                        if (SyntaxLispElementType.REF.isTypeMatch(dpf)) {
                            DefID dpfDef = defIDMap.get(dpf.getVal());
                            if (!res.contains(dpfDef)) {
                               res.add(dpfDef);
                               next.offer(dpfDef);
                            }
                        }
                   });
            }
        }
        return res;
    }

    public static void genNextAndUpperAndBelongsToDef(Map<String, SyntaxLispElement> env) {
        env.forEach((def, ele) -> genNextAndUpperAndBelongsToDef(ele, def));
        env.forEach((def, ele) -> ele.setCanSkipToUpperNext(true));
    }

    public static void genNextAndUpperAndBelongsToDef(SyntaxLispElement cur, String belongsToDef) {
        cur.setBelongsToDef(belongsToDef);
        if (isNoTerminalType(cur)) {
            switch (cur.getType()) {
                case ALTERNATIVE: {
                    cur.getSubElements().forEach(sub -> {
                        sub.setUpper(cur);
                        genNextAndUpperAndBelongsToDef(sub, belongsToDef);
                        sub.setCanSkipToUpperNext(true);
                    });
                    break;
                }
                case SIMPLIFY_REPEAT:
                case NOT_OR: {
                    cur.getSub(0).setUpper(cur);
                    genNextAndUpperAndBelongsToDef(cur.getSub(0), belongsToDef);
                    cur.getSub(0).setCanSkipToUpperNext(true);
                    break;
                }
                case SEQ_CONNECT: {
                    for (int i = 0; i < cur.getSubsSize(); i ++) {
                        SyntaxLispElement curSub = cur.getSub(i);
                        curSub.setUpper(cur);
                        curSub.setDirectNext(new ArrayList<>());
                        int j = i + 1;
                        while (j < cur.getSubsSize()
                                && !cur.getSub(j).isCritical()) {
                            curSub.getDirectNext().add(cur.getSub(j));
                            j += 1;
                        }
                        if (j < cur.getSubsSize()) {
                            curSub.getDirectNext().add(cur.getSub(j));
                        }
                        curSub.setCanSkipToUpperNext(j >= cur.getSubsSize());
                        genNextAndUpperAndBelongsToDef(curSub, belongsToDef);
                    }
                    break;
                }
                default: {
                    throw new MatchException("Unknown type " + cur.getType() + " while genNextAndUpper.");
                }
            }
        }
    }

    public static Map<DefID, Set<SyntaxLispElement>> getPfCrEnv(Map<String, SyntaxLispElement> env,
                                                                Function<SyntaxLispElement, Set<SyntaxLispElement>>
                                                                         getter,
                                                                Map<String, DefID> defIDMap) {
        Map<DefID, Set<SyntaxLispElement>> res = new HashMap<>(env.size());
        env.forEach((def, element) -> res.put(defIDMap.get(def), getter.apply(element)));
        return res;
    }

    public static CriticalProcessingBeforeMatchRes criticalProcessingWithPriority(Map<String, SyntaxLispElement> env,
                                                                                  Map<String, DefID> defIDMap) {

        CriticalProcessingBeforeMatchRes res = criticalProcessingBeforeMatch(env, true, false, defIDMap);

        env.forEach((def, body) -> determineDirectPriorityPfList(body));

        env.keySet().forEach(def -> determineBasePriorityPfList(env.get(def), env));

        env.forEach((def, body) -> {
            if (isAccessibleNoTerminalType(body)) {
                body.getSubElements()
                    .forEach(sub -> determineSubElementBasePriorityPfList(sub, env));
            }
        });

        env.forEach((def, body) -> determineBasePriorityNextList(body, env));

        return res;
    }

    public static void determineBasePriorityPfList (SyntaxLispElement cur, Map<String, SyntaxLispElement> env) {

        if (cur.getBasePriorityPfList() == null) {
            cur.setBasePriorityPfList(new ArrayList<>());

            cur.getDirectPriorityPfList()
                   .forEach(curPriorityDpfSet -> {
                       List<Map<SyntaxLispElement, Set<SyntaxLispElement>>> curExpandPriority = new ArrayList<>();
                       curExpandPriority.add(new HashMap<>());
                       for (SyntaxLispElement pf : curPriorityDpfSet) {
                           if (REF.isTypeMatch(pf)) {
                               determineBasePriorityPfList (env.get(pf.getVal()), env);

                               List<Map<SyntaxLispElement, Set<SyntaxLispElement>>> inheritPriorityList
                                       = env.get(pf.getVal()).getBasePriorityPfList();

                               List<Map<SyntaxLispElement, Set<SyntaxLispElement>>> curList
                                       = new ArrayList<>(inheritPriorityList.size());

                               for (Map<SyntaxLispElement, Set<SyntaxLispElement>> inheritMap : inheritPriorityList) {
                                   Map<SyntaxLispElement, Set<SyntaxLispElement>> curMap = new HashMap<>(inheritMap.size());
                                   inheritMap.keySet().forEach(basePf -> curMap.put(basePf, CommonUtils.asWritableSet(pf)));
                                   curList.add(curMap);
                               }

                               mergePriorityMap (curExpandPriority, curList);

                           } else {
                               curExpandPriority.get(0).computeIfAbsent(pf, k -> new HashSet<>()).add(pf);
                           }
                       }
                       appendPriorityMap(cur.getBasePriorityPfList(), curExpandPriority);
                   });
        }
    }

    public static void determineSubElementBasePriorityPfList (SyntaxLispElement cur,
                                                              Map<String, SyntaxLispElement> env) {

        if (SyntaxLispElementType.isNoTerminalType(cur)) {
            switch (cur.getType()) {
                case ALTERNATIVE:
                case SIMPLIFY_REPEAT:
                case SEQ_CONNECT: {
                    cur.getSubElements().forEach(sub -> determineSubElementBasePriorityPfList(sub, env));
                    break;
                }
                default: {
                    throw new ParseException("determineBasePriorityPfList 操作不支持 element 类型:" + cur.getType());
                }
            }
        }
        if (cur.getBasePriorityPfList() == null) {
            determineBasePriorityPfList (cur, env);
        }
    }

    private static boolean isAccessibleNoTerminalType(SyntaxLispElement cur) {
        return ALTERNATIVE == cur.getType() || SIMPLIFY_REPEAT == cur.getType() || SEQ_CONNECT == cur.getType();
    }

    private static void determineBasePriorityNextList (SyntaxLispElement cur, Map<String, SyntaxLispElement> env) {
        if (!CollectionUtils.isEmpty(cur.getDirectPriorityNextList())) {
            cur.setBasePriorityNextList(new ArrayList<>());
            cur.getDirectPriorityNextList().forEach(priorityNextList -> {
                List<Map<SyntaxLispElement, Set<SyntaxLispElement>>> curExpandPriority = new ArrayList<>();
                for (SyntaxLispElement next : priorityNextList) {
                    determineBasePriorityPfList(next, env);
                    mergePriorityMap(curExpandPriority, next.getBasePriorityPfList());
                }
                appendPriorityMap(cur.getBasePriorityNextList(), curExpandPriority);
            });
        }
        if (SyntaxLispElementType.isNoTerminalType(cur)) {
            switch (cur.getType()) {
                case ALTERNATIVE:
                case SIMPLIFY_REPEAT:
                case SEQ_CONNECT: {
                    cur.getSubElements().forEach(sub -> determineBasePriorityNextList(sub, env));
                    break;
                }
                default: {
                    throw new ParseException("determineBasePriorityNextList 操作不支持 element 类型:" + cur.getType());
                }
            }
        }
    }

    public static void determineDirectPriorityPfList (SyntaxLispElement cur) {
        if (SyntaxLispElementType.isBaseType(cur)) {
            cur.setDirectPriorityPfList(CommonUtils.singletonList(CommonUtils.asWritableSet(cur)));
        } else {
            cur.getSubElements().forEach(PreMatchingProcess::determineDirectPriorityPfList);
            cur.setDirectPriorityPfList(new ArrayList<>());
            switch (cur.getType()) {
                case ALTERNATIVE: {
                    cur.getSubElements().forEach(sub -> merge(cur.getDirectPriorityPfList(),
                                                              sub.getDirectPriorityPfList()));
                    break;
                }
                case SIMPLIFY_REPEAT: {
                    cur.getDirectPriorityPfList()
                       .addAll(((SimplifyRepeatElement) cur).getRepeatBody()
                                                            .getDirectPriorityPfList());
                    break;
                }
                case SEQ_CONNECT: {
                    boolean criticalEnd = false;
                    for (int i = 0;
                         i < cur.getSubsSize() && !criticalEnd;
                         i ++) {
                        List<Set<SyntaxLispElement>> hungerSegment = new ArrayList<>();
                        SyntaxLispElement sub = cur.getSub(i);
                        do {
                            merge(hungerSegment, sub.getDirectPriorityPfList());
                            if (!sub.isHunger()
                                    && !sub.isCritical()
                                    && (i + 1) < cur.getSubsSize()) {
                                i += 1;
                                sub = cur.getSub(i);
                            } else {
                                criticalEnd = sub.isCritical();
                                break;
                            }
                        } while (true);
                        append(cur.getDirectPriorityPfList(), hungerSegment);
                    }
                    break;
                }
                default: {
                    throw new ParseException("determineDirectPriorityPfList 操作不支持 element 类型:" + cur.getType());
                }
            }
        }
        if (!CollectionUtils.isEmpty(cur.getDirectNext())) {
            cur.setDirectPriorityNextList(new ArrayList<>());
            for (int i = 0; i < cur.getDirectNext().size(); i ++) {
                Set<SyntaxLispElement> hungerSegment = new HashSet<>();
                SyntaxLispElement sub = cur.getDirectNext().get(i);
                do {
                    hungerSegment.add(sub);
                    if (!sub.isHunger() && (i + 1) < cur.getDirectNext().size()) {
                        i += 1;
                        sub = cur.getDirectNext().get(i);
                    } else {
                        break;
                    }
                } while (true);
                cur.getDirectPriorityNextList().add(hungerSegment);
            }
        }
    }

    private static <T> void append (List<Set<T>> src, List<Set<T>> appendTarget) {
        src.addAll(appendTarget);
    }

    private static <K, V> void appendPriorityMap (List<Map<K, V>> src, List<Map<K, V>> appendTarget) {
        src.addAll(appendTarget);
    }

    private static <T> void merge (List<Set<T>> src, List<Set<T>> mergeTarget) {
        for (int i = 0; i < src.size() && i < mergeTarget.size(); i ++) {
            src.get(i).addAll(mergeTarget.get(i));
        }
        if (src.size() < mergeTarget.size()) {
            for (int i = src.size(); i < mergeTarget.size(); i ++) {
                src.add(new HashSet<>(mergeTarget.get(i)));
            }
        }
    }

    private static <T> void mergePriorityMap (List<Map<T, Set<T>>> src, List<Map<T, Set<T>>> mergeTarget) {
        int priority = 0;
        while (priority < src.size() && priority < mergeTarget.size()) {
            int finalPriority = priority;
            mergeTarget.get(priority).forEach((matchElement, locateSet) -> {
                src.get(finalPriority)
                   .computeIfAbsent(matchElement, k -> new HashSet<>())
                   .addAll(locateSet);
            });
            priority += 1;
        }
        while (priority < mergeTarget.size()) {
            src.add(new HashMap<>(mergeTarget.get(priority)));
            priority += 1;
        }
    }

    public static Map<DefID, Set<SyntaxLispElement>> determinePossibleContains (Map<String, SyntaxLispElement> env,
                                                                                Map<String, DefID> defIDMap) {
        // step1: 找到每个 def 的 only-occur-once direct possible first set
        Map<DefID, Set<SyntaxLispElement>> onlyOccurOnceDirectFirstMap = onlyOccurOnceDirectFirst(env, defIDMap);

        // step2: 找到每个 def 的 complete direct possible contains
        Map<DefID, Set<SyntaxLispElement>> completeDirectPossibleContainsMap
                = completeDirectPossibleContains(env, defIDMap);

        // step3: 从 complete possible contains 中将 only-occur-once direct possible first set 减去得到 pure direct possible contains
        Map<DefID, Set<SyntaxLispElement>> pureDirectPossibleContainsMap = new HashMap<>(env.size());
        env.forEach((def, body) -> {
            DefID curDef = defIDMap.get(def);
            pureDirectPossibleContainsMap
                    .put(curDef,
                         new HashSet<>(CollectionUtils.subtract(completeDirectPossibleContainsMap.get(curDef),
                                                                onlyOccurOnceDirectFirstMap.getOrDefault(curDef, Collections.emptySet()))));
        });

        // step4: 通过 complete direct possible contains 计算引用连通分量 complete possible contain component map
        // 计算 ref 强连通分量
        Map<DefID, Component> cdpcRefComponentMap
                = collectRefComponentMap(getRefMap(completeDirectPossibleContainsMap), env, false, defIDMap);

        // step5: 通过 complete possible contain component map 同时计算 component 中 def 的 complete base possible contains
        Map<DefID, Set<SyntaxLispElement>> collectComponentCBPC
                = collectComponentCBPC (cdpcRefComponentMap, completeDirectPossibleContainsMap, defIDMap);

        // step6: 完善计算 complete base possible contains（对所有不在 def 所处 component 中的 ref 做计算）
        Map<DefID, Set<SyntaxLispElement>> cbpcMap = collectNoComponentCBPC (collectComponentCBPC, defIDMap);

        // step7: 完善计算 pure base possible contains
        Map<DefID, Set<SyntaxLispElement>> pureBasePossibleContainsMap
                = determinePbpc (pureDirectPossibleContainsMap, cbpcMap, defIDMap);

        return pureBasePossibleContainsMap;
    }

    private static Map<DefID, Set<SyntaxLispElement>>
    determinePbpc (Map<DefID, Set<SyntaxLispElement>> pureDirectPossibleContainsMap,
                   Map<DefID, Set<SyntaxLispElement>> cbpcMap,
                   Map<String, DefID> defIDMap) {

        Map<DefID, Set<SyntaxLispElement>> pureBasePossibleContainsMap
                = new HashMap<>(pureDirectPossibleContainsMap.size());
        pureDirectPossibleContainsMap.forEach((def, pdpc) -> {
            Set<SyntaxLispElement> basePossibleContains = new HashSet<>();
            pdpc.forEach(directPossibleContain -> {
                if (directPossibleContain.isRef()) {
                    DefID directPossibleContainDef = defIDMap.get(directPossibleContain.getVal());
                    basePossibleContains.addAll(cbpcMap.getOrDefault(directPossibleContainDef, Collections.emptySet()));
                } else {
                    basePossibleContains.add(directPossibleContain);
                }
            });
            pureBasePossibleContainsMap.put(def, basePossibleContains);
        });
        return pureBasePossibleContainsMap;
    }

    private static Map<DefID, Set<SyntaxLispElement>>
    collectNoComponentCBPC (Map<DefID, Set<SyntaxLispElement>> cbpcMap, Map<String, DefID> defIDMap) {
        Map<DefID, Set<SyntaxLispElement>> newCbpcMap = new HashMap<>(cbpcMap.size());
        cbpcMap.forEach((def, cbpc) -> collectNoComponentCBPC(def, cbpcMap, newCbpcMap, defIDMap));
        return newCbpcMap;
    }

    public static void collectNoComponentCBPC (DefID curDef,
                                               Map<DefID, Set<SyntaxLispElement>> cbpcMap,
                                               Map<DefID, Set<SyntaxLispElement>> finalCbpcMap,
                                               Map<String, DefID> defIDMap) {
        Set<SyntaxLispElement> oldCbpc = cbpcMap.get(curDef);
        if (!CollectionUtils.isEmpty(oldCbpc)) {
            Set<SyntaxLispElement> newCbpc = new HashSet<>();
            oldCbpc.forEach(possibleContain -> {
                if (possibleContain.isRef()) {
                    DefID pcRef = defIDMap.get(possibleContain.getVal());
                    if (!finalCbpcMap.containsKey(pcRef)) {
                        collectNoComponentCBPC (pcRef, cbpcMap, finalCbpcMap, defIDMap);
                    }
                    newCbpc.addAll(finalCbpcMap.getOrDefault(pcRef, Collections.emptySet()));
                } else {
                    newCbpc.add(possibleContain);
                }
            });
            finalCbpcMap.put(curDef, newCbpc);
        }
    }

    private static Map<DefID, Set<SyntaxLispElement>>
    collectComponentCBPC (Map<DefID, Component> cdpcRefComponentMap,
                          Map<DefID, Set<SyntaxLispElement>> cdpcMap,
                          Map<String, DefID> defIDMap) {

        Map<DefID, Set<SyntaxLispElement>> cbpcMap = new HashMap<>();
        cdpcRefComponentMap.values().forEach(component -> {
            Set<SyntaxLispElement> componentBaseContains = new HashSet<>();
            component.getDefSet().forEach(componentDef -> {
                Set<SyntaxLispElement> directContains = cdpcMap.get(componentDef);
                if (!CollectionUtils.isEmpty(directContains)) {
                    directContains.forEach(directContain -> {
                        if (!directContain.isRef()) {
                            DefID directContainDef = defIDMap.get(directContain.getVal());
                            if (!component.contains(directContainDef)) {
                                componentBaseContains.add(directContain);
                            }
                        }
                    });
                }
            });
            component.getDefSet()
                     .forEach(componentDef -> cbpcMap.put(componentDef, componentBaseContains));
        });
        cdpcMap.forEach((def, cdpc) -> {
            if (!cdpcRefComponentMap.containsKey(def)) {
                cbpcMap.put(def, cdpc);
            }
        });
        return cbpcMap;
    }

    private static Map<DefID, Set<SyntaxLispElement>>
    completeDirectPossibleContains (Map<String, SyntaxLispElement> env, Map<String, DefID> defIDMap) {
        Map<DefID, Set<SyntaxLispElement>> res = new HashMap<>(env.size());
        env.forEach((def, body) -> {
            Set<SyntaxLispElement> curContains = new HashSet<>();
            completeDirectPossibleContains(body, curContains);
            res.put(defIDMap.get(def), curContains);
        });
        return res;
    }

    private static void completeDirectPossibleContains (SyntaxLispElement cur, Set<SyntaxLispElement> containSet) {
        if (SyntaxLispElementType.isBaseType(cur)) {
            containSet.add(cur);
        } else {
            switch (cur.getType()) {
                case ALTERNATIVE:
                case SEQ_CONNECT: {
                    cur.getSubElements()
                       .forEach(sub -> completeDirectPossibleContains(sub, containSet));
                    break;
                }
                case SIMPLIFY_REPEAT: {
                    completeDirectPossibleContains(cur.getSub(0), containSet);
                    break;
                }
                default: {
                    throw new ParseException("UnKnow type " + cur.getType() + " while completeDirectPossibleContains");
                }
            }
        }
    }

    private static Map<DefID, Set<SyntaxLispElement>> onlyOccurOnceDirectFirst (Map<String, SyntaxLispElement> env,
                                                                                Map<String, DefID> defIDMap) {
        Map<DefID, Set<SyntaxLispElement>> res = new HashMap<>(env.size());
        env.forEach((def, body) -> {
            body.getDirectPf().forEach(pf -> {
                if (isOnlyOccurOnceFirst(pf)) {
                    res.computeIfAbsent(defIDMap.get(def), d -> new HashSet<>())
                       .add(pf);
                }
            });
        });
        return res;
    }

    private static boolean isOnlyOccurOnceFirst (SyntaxLispElement first) {
        SyntaxLispElement cur = first.getUpper();
        while (cur != null) {
            if (cur.isRepeat()) {
                SimplifyRepeatElement v = (SimplifyRepeatElement) cur;
                if (v.getMaxMatchTime() > 1
                        || (v.getMaxMatchTime() == SimplifyRepeatElement.INFINITY)) {
                    return false;
                }
            }
            cur = cur.getUpper();
        }
        return true;
    }

    public static CriticalProcessingBeforeMatchRes criticalProcessingBeforeMatch(Map<String, SyntaxLispElement> env,
                                                                                 boolean forbiddenFirstRefCycle,
                                                                                 boolean reverse,
                                                                                 Map<String, DefID> defIDMap) {
        // 计算 dpf 以及 dcr
        Map<String, PfCr> dPfCrEnv = new HashMap<>();
        env.forEach((defName, element) -> {
            directPossibleAndCriticalFirst(element, reverse);
            dPfCrEnv.put(defName, getDirectPfCr(element, reverse));
        });

        if (forbiddenFirstRefCycle) {
            List<RefCycle> refCycles = new ArrayList<>();
            Map<DefID, Set<ReferenceElement>> leftRefMap
                    = getRefMap(getPfCrEnv(env, reverse ? SyntaxLispElement::getDirectPl
                                                        : SyntaxLispElement::getDirectPf,
                                           defIDMap));
            env.keySet().forEach(def -> {
                DefID curDef = defIDMap.get(def);
                collectRefCycle(curDef, leftRefMap, new ArrayList<>(), refCycles, defIDMap);
            });
            if (!refCycles.isEmpty()) {
                throw new ParseException("存在首引用环路，需要先消除：" + refCycles);
            }
        }

        // 计算 ref 强连通分量
        Map<DefID, Component> dpfRefComponentMap
                = collectRefComponentMap(getRefMap(getPfCrEnv(env, reverse ? SyntaxLispElement::getDirectPl
                                                                           : SyntaxLispElement::getDirectPf,
                                                              defIDMap)), env,
                                         true,
                                         defIDMap);

        Map<DefID, Set<SyntaxLispElement>> defPfMap = new HashMap<>();
        determineDefBPfCr(dpfRefComponentMap, env, true, defPfMap, reverse, defIDMap);
        env.keySet().forEach(def -> determineBPfCr(env.get(def), defPfMap, true, reverse, defIDMap));

/*        Map<String, Component> dcrRefComponentMap
                = collectRefComponentMap(getRefMap(getPfCrEnv(env, SyntaxLispElement::getDirectCr)), false);*/

        if (!reverse) {
            Map<DefID, Set<SyntaxLispElement>> defCrMap = new HashMap<>();
            determineDefBPfCr(dpfRefComponentMap, env, false, defCrMap, reverse, defIDMap);
            env.keySet().forEach(def -> determineBPfCr(env.get(def), defCrMap, false, reverse, defIDMap));

            genNextAndUpperAndBelongsToDef(env);

            checkDeadFirstRefReduceCycle(env);

            Map<DefID, Set<DefID>> firstRefReduceMap = firstRefReduceMap(env, defIDMap);
            Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceMidSetMap = new HashMap<>();
            Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceDirectToMap = new HashMap<>();
            env.keySet().forEach(def -> {
                DefID curDef = defIDMap.get(def);
                genFirstRefReduceMidSetMap(curDef, env, firstRefReduceMidSetMap,
                                           firstRefReduceDirectToMap, new ArrayList<>(),
                                           defIDMap);
            });
            return CriticalProcessingBeforeMatchRes.builder()
                                                   .defPfMap(defPfMap)
                                                   .firstRefReduceMap(firstRefReduceMap)
                                                   .reduceToFirstRefMap(parseToReduceToFirstRefMap(firstRefReduceMap))
                                                   .firstRefReduceMidSetMap(firstRefReduceMidSetMap)
                                                   .firstRefReduceDirectToMap(firstRefReduceDirectToMap)
                                                   .build();
        } else {
            return CriticalProcessingBeforeMatchRes.builder()
                                                   .defPfMap(defPfMap)
                                                   .build();
        }
    }

    public static void genFirstRefReduceMidSetMap (DefID curDef,
                                                   Map<String, SyntaxLispElement> env,
                                                   Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceMidSetMap,
                                                   Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceDirectToMap,
                                                   List<DefID> firstRefPath,
                                                   Map<String, DefID> defIDMap) {

        SyntaxLispElement curDefElement = env.get(curDef.getDef());
        if (!firstRefPath.isEmpty()) {
            DefID lastRef = CommonUtils.top(firstRefPath).get();
            for (int i = 0; i < firstRefPath.size(); i ++) {
                DefID start = firstRefPath.get(i);
                Set<DefID> midSet = firstRefReduceMidSetMap.computeIfAbsent(start, f -> new HashMap<>())
                                                           .computeIfAbsent(curDef, e -> new HashSet<>());
                if (i < (firstRefPath.size() - 1)) {
                    midSet.addAll(firstRefPath.subList(i + 1, firstRefPath.size()));
                }
                midSet.add(curDef);
                Set<DefID> directToSet = firstRefReduceDirectToMap.computeIfAbsent(start, f -> new HashMap<>())
                                                                  .computeIfAbsent(curDef, e -> new HashSet<>());
                directToSet.add(lastRef);
            }
        }
        CommonUtils.push(firstRefPath, curDef);
        curDefElement.getDirectPf().forEach(df -> {
            if (REF.isTypeMatch(df)) {
                DefID dfDef = defIDMap.get(df.getVal());
                genFirstRefReduceMidSetMap(dfDef, env, firstRefReduceMidSetMap, firstRefReduceDirectToMap,
                                           firstRefPath, defIDMap);
            }
        });
        CommonUtils.pop(firstRefPath);
    }

    private static Map<DefID, Set<ReferenceElement>> getRefMap (Map<DefID, Set<SyntaxLispElement>> pfCrEnv) {
        Map<DefID, Set<ReferenceElement>> refMap = new HashMap<>(pfCrEnv.size());
        pfCrEnv.forEach((def, pfCr) -> {
            Set<ReferenceElement> refs = new HashSet<>();
            pfCr.forEach(pf -> {
                if (SyntaxLispElementType.REF.isTypeMatch(pf)
                        && refs.stream().noneMatch(ref -> ref.isSameRef(pf))) {
                    refs.add((ReferenceElement) pf);
                }
            });
            refMap.put(def, refs);
        });
        return refMap;
    }

    @AllArgsConstructor
    private static class RefCycle {

        List<DefID> cycle;

        public boolean equals (Object o) {
            if (!(o instanceof RefCycle)) {
                return false;
            }
            return CollectionUtils.isEqualCollection(cycle, ((RefCycle) o).cycle);
        }

        public String toString () {
            return cycle.toString();
        }
    }

    public static Map<DefID, Component> collectRefComponentMap(Map<DefID, Set<ReferenceElement>> defRefMap,
                                                               Map<String, SyntaxLispElement> env,
                                                               boolean checkCloseComponent,
                                                               Map<String, DefID> defIDMap) {
        Map<DefID, Component> defComponentMap = new HashMap<>(defRefMap.size());
//        Set<DefID> visited = new HashSet<>();

/*        defRefMap.forEach((def, refSet) -> collectRefComponentMap(def, new ArrayList<>(), defRefMap, defComponentMap,
                                                                  defIDMap, visited));*/
        LambdaInteger visitOrderGenerator  = new LambdaInteger(1);
        Map<DefID, Integer> visitOrderMap = new HashMap<>(defRefMap.size());
        Map<DefID, Integer> lowestMap = new HashMap<>(defRefMap.size());
        List<DefID> stack = new ArrayList<>(defRefMap.size());
        Set<DefID> poppedDef = new HashSet<>(defRefMap.size());
        defRefMap.forEach((def, refSet) -> {
            if (!visitOrderMap.containsKey(def)) {
                collectRefComponentMap(def, defRefMap, defIDMap, visitOrderGenerator,
                                       visitOrderMap, lowestMap, stack, poppedDef,
                                       defComponentMap);
            }
        });

        if (checkCloseComponent) {
            Map<Component, Boolean> componentCloseStatusMap = new HashMap<>();
            for (Component c : defComponentMap.values()) {
                CommonUtils.checkOrThrow(!isClosedComponent(c, env, defComponentMap, componentCloseStatusMap, defIDMap),
                                         "完全连通分量的 pf 不能封闭: " + c.getDefSet());
            }
        }
        return defComponentMap;
    }

    public static Integer collectRefComponentMap (DefID curDef,
                                              Map<DefID, Set<ReferenceElement>> defRefMap,
                                              Map<String, DefID> defIDMap,
                                              LambdaInteger visitOrderGenerator,
                                              Map<DefID, Integer> visitOrderMap,
                                              Map<DefID, Integer> lowestMap,
                                              List<DefID> stack,
                                              Set<DefID> poppedDef,
                                              Map<DefID, Component> collect) {
        stack.add(curDef);
        Integer curVisitOrder = visitOrderGenerator.getAndIncrease();
        visitOrderMap.put(curDef, curVisitOrder);
        LambdaBoolean selfRef = new LambdaBoolean(false);
        LambdaInteger lowest = new LambdaInteger(curVisitOrder);
        defRefMap.get(curDef)
                 .forEach(subRef -> {
                     DefID subRefDef = defIDMap.get(subRef.getVal());
                     selfRef.set(selfRef.get() || (subRefDef.equals(curDef)));
                     if (stack.contains(subRefDef)) {
                         // in stack
                         Integer subRefVisitOrder = visitOrderMap.get(subRefDef);
                         lowest.setVal(Math.min(lowest.getVal(), subRefVisitOrder));
                     } else {
                         if (!visitOrderMap.containsKey(subRefDef)) {
                             Integer subLowest
                                     = collectRefComponentMap(subRefDef, defRefMap, defIDMap, visitOrderGenerator,
                                                              visitOrderMap, lowestMap, stack, poppedDef, collect);
                             lowest.setVal(Math.min(lowest.getVal(), subLowest));
                         }
                     }
                 });
        if (lowest.getVal() == curVisitOrder) {
            // 确定强连通分量, 收集该连通分量
            List<DefID> cycle = stack.subList(stack.indexOf(curDef), stack.size());
            if (cycle.size() > 1 || selfRef.get()) {
                Component newComponent = new Component(cycle);
                newComponent.getDefSet()
                            .forEach(componentDef -> {
                                collect.put(componentDef, newComponent);
                            });
            }
            cycle.clear();
        }
        lowestMap.put(curDef, lowest.getVal());
        return lowest.getVal();
    }

    /*public static void collectRefComponentMap(DefID curDef,
                                              List<DefID> refPath,
                                              Map<DefID, Set<ReferenceElement>> defRefMap,
                                              Map<DefID, Component> collect,
                                              Map<String, DefID> defIDMap,
                                              Set<DefID> visited) {
        visited.add(curDef);
        refPath.add(curDef);

        defRefMap.get(curDef)
                 .forEach(subRef -> {
                     DefID refDef = defIDMap.get(subRef.getVal());
                     if (refPath.contains(refDef)) {
                         // 确定引用回路, 收集该回路
                         List<DefID> cycle = refPath.subList(refPath.indexOf(refDef), refPath.size());
                         Component newComponent = new Component(cycle);
                         Set<Component> history = new HashSet<>();
                         history.add(newComponent);

                         for (DefID cycleDef : cycle) {
                             if (collect.containsKey(cycleDef)) {
                                 Component old = collect.get(cycleDef);
                                 if (!history.contains(old)) {
                                     newComponent.union(old);
                                     history.add(old);
                                 }
                             }
                         }
                         for (DefID cycleDef : newComponent.getDefSet()) {
                             collect.put(cycleDef, newComponent);
                         }

                     }
                     if (!visited.contains(refDef)) {
                         collectRefComponentMap(refDef, refPath, defRefMap, collect, defIDMap, visited);
                     }
                 });
        refPath.remove(curDef);
    }*/

    @Builder
    @Data
    public static class CriticalProcessingBeforeMatchRes {

        private Map<DefID, Set<SyntaxLispElement>> defPfMap;

        private Map<DefID, Set<DefID>> firstRefReduceMap;

        private Map<DefID, Set<DefID>> reduceToFirstRefMap;

        private Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceMidSetMap;

        private Map<DefID, Map<DefID, Set<DefID>>> firstRefReduceDirectToMap;
    }

    private static Map<DefID, Set<DefID>> parseToReduceToFirstRefMap (Map<DefID, Set<DefID>> firstRefReduceMap) {
        Map<DefID, Set<DefID>> reduceToFirstRefMap = new HashMap<>();
        firstRefReduceMap.forEach((def, reduceSet) -> {
            for (DefID reduceDef : reduceSet) {
                if (!reduceToFirstRefMap.containsKey(reduceDef)) {
                    Set<DefID> curReduceSet = new HashSet<>();
                    firstRefReduceMap.forEach((def0, reduceSet0) -> {
                        if (reduceSet0.contains(reduceDef)) {
                            curReduceSet.add(def0);
                        }
                    });
                    reduceToFirstRefMap.put(reduceDef, curReduceSet);
                }
            }
        });
        return reduceToFirstRefMap;
    }

    /**
     * 计算 Syntax-lisp 节点 Base Possible First
     * @param cur Syntax-lisp 节点
     * @param defBPfCrMap Definition possible first Map
     */
    private static void determineBPfCr(SyntaxLispElement cur,
                                       Map<DefID, Set<SyntaxLispElement>> defBPfCrMap,
                                       boolean isPf,
                                       boolean reverse,
                                       Map<String, DefID> defIDMap) {

        if (isNoTerminalType(cur.getType()) || REF == cur.getType()) {

            Set<SyntaxLispElement> nwBpfCrs = new HashSet<>();
            Set<SyntaxLispElement> dPfCrs = isPf ? getDirectPf(cur, reverse) : getDirectCr(cur, reverse);
            dPfCrs.forEach(dPfCr -> {
                if (REF.isTypeMatch(dPfCr)) {
                    DefID dPfCrDef = defIDMap.get(dPfCr.getVal());
                    nwBpfCrs.addAll(defBPfCrMap.get(dPfCrDef));
                } else {
                    nwBpfCrs.add(dPfCr);
                }
            });
            if (isPf) {
                setBasePfCr(cur, PfCr.builder().possibleSet(nwBpfCrs).build(), reverse);
            } else {
                setBasePfCr(cur, PfCr.builder().criticalSet(nwBpfCrs).build(), reverse);
            }
            switch (cur.getType()) {
                case ALTERNATIVE:
                case SEQ_CONNECT: {
                    cur.getSubElements()
                       .forEach(sub -> determineBPfCr(sub, defBPfCrMap, isPf, reverse, defIDMap));
                    break;
                }

                case NOT_OR:
                case SIMPLIFY_REPEAT: {
                    determineBPfCr(cur.getSub(0), defBPfCrMap, isPf, reverse, defIDMap);
                    break;
                }

                case REF: {
                    break;
                }

                default: {
                    throw new ParseException("");
                }
            }
        } else {
            setBasePfCr(cur, cur.getDirectPfCr(), reverse);
        }
    }

    private static void determineDefBPfCr(Map<DefID, Component> dpfRefComponentMap,
                                          Map<String, SyntaxLispElement> env,
                                          boolean isPf,
                                          Map<DefID, Set<SyntaxLispElement>> defBPfCrMapCollect,
                                          boolean isReverse,
                                          Map<String, DefID> defIDMap) {
        // 每个 component 中的 def 拥有相同的 bpf or bcr
        // 从 component 中 每个 def 出发找到引用的
        Map<Component, Set<SyntaxLispElement>> componentBPfCrMapCollect = new HashMap<>();

        for (Component component : dpfRefComponentMap.values()) {
            determineComponentBpfCr(component, dpfRefComponentMap, env, isPf, defBPfCrMapCollect,
                                    componentBPfCrMapCollect, new ArrayList<>(), new ArrayList<>(), isReverse,
                                    defIDMap);
        }

        componentBPfCrMapCollect.forEach((c, s) -> c.getDefSet()
                                                    .forEach(def -> defBPfCrMapCollect.put(def, s)));

        env.forEach((def, body) -> {
            DefID curDef = defIDMap.get(def);
            if (!defBPfCrMapCollect.containsKey(curDef)) {
                determineDefBpfCr(curDef, dpfRefComponentMap, env, isPf, defBPfCrMapCollect, componentBPfCrMapCollect,
                                  new ArrayList<>(), new ArrayList<>(), isReverse, defIDMap);
            }
        });
    }

    private static Set<SyntaxLispElement> determineComponentBpfCr (Component cur,
                                                                   Map<DefID, Component> dpfRefComponentMap,
                                                                   Map<String, SyntaxLispElement> env,
                                                                   boolean isPf,
                                                                   Map<DefID, Set<SyntaxLispElement>>
                                                                           defBPfCrMapCollect,
                                                                   Map<Component, Set<SyntaxLispElement>>
                                                                           componentBPfCrMapCollect,
                                                                   List<DefID> refPath,
                                                                   List<Component> componentPath,
                                                                   boolean isReverse,
                                                                   Map<String, DefID> defIDMap) {
        if (componentPath.contains(cur)) {
            return Collections.emptySet();
        }
        if (!componentBPfCrMapCollect.containsKey(cur)) {
            componentPath.add(cur);
            Set<SyntaxLispElement> res = new HashSet<>();
            for (DefID curDef : cur.getDefSet()) {
                for (SyntaxLispElement dpf : isPf
                                             ? (isReverse ? env.get(curDef.getDef()).getDirectPl()
                                                          : env.get(curDef.getDef()).getDirectPf())
                                             : env.get(curDef.getDef()).getDirectCr()) {
                    if (REF.isTypeMatch(dpf)) {
                        DefID dpfRef = defIDMap.get(dpf.getVal());
                        res.addAll(determineDefBpfCr(dpfRef, dpfRefComponentMap, env, isPf, defBPfCrMapCollect,
                                                     componentBPfCrMapCollect, refPath, componentPath, isReverse,
                                                     defIDMap));
                    } else {
                        res.add(dpf);
                    }
                }
            }
            componentBPfCrMapCollect.put(cur, res);
            componentPath.remove(cur);
        }
        return componentBPfCrMapCollect.get(cur);
    }

    private static Set<SyntaxLispElement> determineDefBpfCr (DefID curDef,
                                                             Map<DefID, Component> dpfRefComponentMap,
                                                             Map<String, SyntaxLispElement> env,
                                                             boolean isPf,
                                                             Map<DefID, Set<SyntaxLispElement>> defBPfCrMapCollect,
                                                             Map<Component, Set<SyntaxLispElement>>
                                                                     componentBPfCrMapCollect,
                                                             List<DefID> refPath,
                                                             List<Component> componentPath,
                                                             boolean isReverse,
                                                             Map<String, DefID> defIDMap) {
        if (refPath.contains(curDef)) {
            return Collections.emptySet();
        }
        if (dpfRefComponentMap.containsKey(curDef)) {
            return determineComponentBpfCr(dpfRefComponentMap.get(curDef), dpfRefComponentMap, env, isPf,
                                           defBPfCrMapCollect, componentBPfCrMapCollect, refPath, componentPath,
                                           isReverse, defIDMap);
        }
        if (!defBPfCrMapCollect.containsKey(curDef)) {
            refPath.add(curDef);
            Set<SyntaxLispElement> res = new HashSet<>();
            for (SyntaxLispElement dpf : isPf
                                         ? (isReverse ? env.get(curDef.getDef()).getDirectPl()
                                                      : env.get(curDef.getDef()).getDirectPf())
                                         : env.get(curDef.getDef()).getDirectCr()) {
                if (REF.isTypeMatch(dpf)) {
                    DefID dpfRef = defIDMap.get(dpf.getVal());
                    res.addAll(determineDefBpfCr(dpfRef, dpfRefComponentMap, env, isPf,
                                                 defBPfCrMapCollect, componentBPfCrMapCollect, refPath, componentPath,
                                                 isReverse, defIDMap));
                } else {
                    res.add(dpf);
                }
            }
            defBPfCrMapCollect.put(curDef, res);
            refPath.remove(curDef);
        }
        return defBPfCrMapCollect.get(curDef);
    }

/*    private static boolean isClosedComponent(Component c,
                                             Map<DefID, Set<ReferenceElement>> refEnv,
                                             Map<String, DefID> defIDMap) {
        for (DefID def : c.getDefSet()) {
            for (ReferenceElement pf : refEnv.get(def)) {
                if (!c.contains(defIDMap.get(pf.getVal()))) {
                    return false;
                }
            }
        }
        return true;
    }*/

    private static boolean isClosedComponent(Component c,
                                             Map<String, SyntaxLispElement> env,
                                             Map<DefID, Component> dpfRefComponentMap,
                                             Map<Component, Boolean> componentCloseStatusMap,
                                             Map<String, DefID> defIDMap) {
        if (!componentCloseStatusMap.containsKey(c)) {
            boolean isClosed = true;
            for (DefID componentDef : c.getDefSet()) {
                for (SyntaxLispElement dpf : env.get(componentDef.getDef()).getDirectPf()) {
                    if (REF.isTypeMatch(dpf)) {
                        DefID refDpf = defIDMap.get(dpf.getVal());
                        if (!c.contains(refDpf)) {
                            isClosed &= dpfRefComponentMap.containsKey(refDpf)
                                            && isClosedComponent(dpfRefComponentMap.get(refDpf), env,
                                                                 dpfRefComponentMap, componentCloseStatusMap, defIDMap);
                        }
                    } else {
                        isClosed = false;
                    }
                    if (!isClosed) { break; }
                }
                if (!isClosed) { break; }
            }
            componentCloseStatusMap.put(c, isClosed);
        }
        return componentCloseStatusMap.get(c);
    }

    @Builder
    @Getter
    private static class Component {

        private final Set<DefID> defSet;

        public Component (Collection<DefID> cycle) {
            defSet = new HashSet<>(cycle);
        }

/*        public boolean hasIntersection(Component other) {
            for (DefID ref : defSet) {
                if (other.getDefSet().contains(ref)) {
                    return true;
                }
            }
            return false;
        }*/

        public void union (Component component) {
            defSet.addAll(component.getDefSet());
        }

        public boolean contains(DefID def) {
            return defSet.contains(def);
        }

        public void add (Component target) {
            defSet.addAll(target.getDefSet());
        }
    }

    /**
     * 收集引用回路
     * @param curRef 当前引用
     * @param defRefEnv def -> dpf 映射
     * @param refPath 引用路径
     * @param collect 引用回路集合
     */
    public static void collectRefCycle(DefID curRef,
                                       Map<DefID, Set<ReferenceElement>> defRefEnv,
                                       List<DefID> refPath,
                                       List<RefCycle> collect,
                                       Map<String, DefID> defIDMap) {
        if (refPath.contains(curRef)) {
            // 确定引用回路, 收集该回路
            List<DefID> srcPath = refPath.subList(refPath.indexOf(curRef), refPath.size());
            RefCycle target = new RefCycle(new ArrayList<>(srcPath));
            if (!collect.contains(target)) {
                collect.add(target);
            }
        } else {
            refPath.add(curRef);
            // 对当前 Ref 的 possible firsts 深搜回路
/*            for (SyntaxLispElement subRef : defRefEnv.get(curRef)) {
                if (REF == subRef.getType()) {
                    collectRefCycle(subRef.getVal(), defRefEnv, refPath, collect);
                }
            }*/
            defRefEnv.get(curRef)
                     .forEach(ref -> {
                         DefID refDef = defIDMap.get(ref.getVal());
                         collectRefCycle(refDef, defRefEnv, refPath, collect, defIDMap);
                     });
            refPath.remove(curRef);
        }
    }

    private static PfCr getDirectPfCr(SyntaxLispElement cur, boolean reverse) {
        return reverse ? cur.getDirectPlCl() : cur.getDirectPfCr();
    }

    private static Set<SyntaxLispElement> getDirectCr (SyntaxLispElement cur, boolean reverse) {
        return getDirectPfCr(cur, reverse).getCriticalSet();
    }

    private static Set<SyntaxLispElement> getDirectPf (SyntaxLispElement cur, boolean reverse) {
        return getDirectPfCr(cur, reverse).getPossibleSet();
    }

    private static void setDirectPfCr(SyntaxLispElement cur, PfCr pfCr, boolean reverse) {
        if (reverse) {
            cur.setDirectPlCl(pfCr);
        } else {
            cur.setDirectPfCr(pfCr);
        }
    }

/*    private static PfCr getBasePfCr(SyntaxLispElement cur, boolean reverse) {
        return reverse ? cur.getBasePlCl() : cur.getBasePfCr();
    }*/

    private static void setBasePfCr(SyntaxLispElement cur, PfCr pfCr, boolean reverse) {
        PfCr srcPfCr = reverse ? cur.getBasePlCl() : cur.getBasePfCr();
        if (srcPfCr != null) {
            srcPfCr.setPossibleSet(KBeanUtils.getOrDefault(pfCr.getPossibleSet(), srcPfCr.getPossibleSet()));
            srcPfCr.setCriticalSet(KBeanUtils.getOrDefault(pfCr.getCriticalSet(), srcPfCr.getCriticalSet()));
        } else {
            if (reverse) {
                cur.setBasePlCl(pfCr);
            } else {
                cur.setBasePfCr(pfCr);
            }
        }
    }

    private static boolean directPossibleAndCriticalFirst(SyntaxLispElement cur, boolean reverse) {
        boolean isCritical = false;
        PfCr res;
        switch (cur.getType()) {
            case NOT_OR: {
                isCritical = directPossibleAndCriticalFirst(cur.getSub(0), reverse);
                res = getDirectPfCr(cur.getSub(0), reverse);
                break;
            }
            case ALTERNATIVE: {
                Set<SyntaxLispElement> pfRes = new HashSet<>();
                Set<SyntaxLispElement> crRes = new HashSet<>();
                isCritical = true;
                for (SyntaxLispElement sub : cur.getSubElements()) {
                    isCritical &= directPossibleAndCriticalFirst(sub, reverse);
                    crRes.addAll(getDirectCr(sub, reverse));
                    pfRes.addAll(getDirectPf(sub, reverse));
                }
                res = PfCr.builder().criticalSet(crRes).possibleSet(pfRes).build();
                break;
            }
            case SIMPLIFY_REPEAT: {
                SimplifyRepeatElement sreCur = (SimplifyRepeatElement) cur;
                isCritical = directPossibleAndCriticalFirst(sreCur.getRepeatBody(), reverse)
                                && (sreCur.getMinMatchTime() > 0);
                res = PfCr.builder()
                          .criticalSet(isCritical
                              ? getDirectCr(sreCur.getRepeatBody(), reverse)
                              : Collections.emptySet())
                          .possibleSet(getDirectPf(sreCur.getRepeatBody(), reverse))
                          .build();
                break;
            }

            case SEQ_CONNECT: {
                Set<SyntaxLispElement> pfRes = new HashSet<>();
                Set<SyntaxLispElement> crRes = new HashSet<>();
                for (int i = reverse ? (cur.getSubsSize() - 1) : 0,
                         c = 0;
                     c < cur.getSubsSize();
                     i += (reverse ? -1 : 1), c ++) {

                    SyntaxLispElement sub = cur.getSub(i);
                    boolean prevIsCritical = isCritical;
                    isCritical |= directPossibleAndCriticalFirst(sub, reverse);
                    if (!isCritical || !prevIsCritical) {
                        pfRes.addAll(getDirectPf(sub, reverse));
                    }
                    crRes.addAll(getDirectCr(sub, reverse));
                }
                res = PfCr.builder().criticalSet(crRes).possibleSet(pfRes).build();
                break;
            }

            case BASE:
            case SCOPE:
            case STRING:
            case CHARACTER:
            case REF: {
                isCritical = true;
                res = PfCr.builder()
                          .criticalSet(Collections.singleton(cur))
                          .possibleSet(Collections.singleton(cur))
                          .build();
                break;
            }
            default: throw new ParseException("Matcher pre-parse 无法解析: " + cur.getType());
        }
        setDirectPfCr(cur, res, reverse);
        return isCritical;
    }
}
